//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-04-05     ZhangYihua   first version
//
// Description  : insert reg between combinational logic domain for better timing
//################################################################################
//
//     mode          src_rdy dst_vld dst_dat  buffer_size   rate    optimise timing for
//"VLD_RS_FULL_RATE"  comb    reg     reg       DAT_BW*1    full      src_vld,src_dat
//"RDY_RS_FULL_RATE"  reg     comb    comb      DAT_BW*1    full      dst_rdy
//"BID_RS_FULL_RATE"  reg     reg    mux-reg    DAT_BW*2    full      all input
//"BID_RS_HALF_RATE"  reg     reg     reg       DAT_BW*1    half      all input

module reg_slicer #(
parameter           RS_MODE                 = "VLD_RS_FULL_RATE",   // timing optimization for src_vld src_dat
//parameter           RS_MODE                 = "RDY_RS_FULL_RATE",   // timing optimization for dst_rdy
//parameter           RS_MODE                 = "BID_RS_FULL_RATE",   // timing optimization for all input
//parameter           RS_MODE                 = "BID_RS_HALF_RATE",   // timing optimization for all input, but half rate pipeline
parameter           DAT_BW                  = 8
) ( 
input                                       rst_n,
input                                       clk,

// a src_dat transmission succeed when src_vld and src_rdy is 1 meanwhile
input                                       src_vld,
input               [DAT_BW-1:0]            src_dat,
output                                      src_rdy,

// a dst_dat transmission succeed when dst_vld and dst_rdy is 1 meanwhile
output                                      dst_vld,
output              [DAT_BW-1:0]            dst_dat,
input                                       dst_rdy
);

//################################################################################
// define local varialbe and localparam
//################################################################################


//################################################################################
// main
//################################################################################

generate if (RS_MODE=="VLD_RS_FULL_RATE") begin:G_VLD_RS_FULL_RATE
    // dst_vld and dst_dat are all reg output
    // src_rdy is combinational logic output
    // data delay is at least 1 clock
    // buffer resource is DAT_BW*1
    // full rate pipeline

    reg                                         buf_empty;
    reg                 [DAT_BW-1:0]            buf_array;
    wire                                        src_rdy_o;

    assign src_rdy_o = buf_empty | dst_rdy;
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_empty <=`U_DLY 1'b1;
            buf_array <=`U_DLY {DAT_BW{1'b0}};
        end
        else begin
            if ((src_rdy_o==1'b1) && (src_vld==1'b1)) begin
                buf_empty <=`U_DLY 1'b0;
                buf_array <=`U_DLY src_dat;
            end
            else if ((buf_empty==1'b0) && (dst_rdy==1'b1))
                buf_empty <=`U_DLY 1'b1;
            else
                ;
        end
    end

    assign src_rdy = src_rdy_o;

    assign dst_vld = ~buf_empty;
    assign dst_dat = buf_array;
    
end
else if (RS_MODE=="BID_RS_HALF_RATE") begin:G_BID_RS_HALF_RATE
    // dst_vld and dst_dat are all reg output
    // src_rdy is reg output
    // data delay is at least 1 clock
    // buffer resource is DAT_BW*1
    // half rate pipeline

    reg                                         buf_empty;
    reg                 [DAT_BW-1:0]            buf_array;
    wire                                        src_rdy_o;

    assign src_rdy_o = buf_empty;
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_empty <=`U_DLY 1'b1;
            buf_array <=`U_DLY {DAT_BW{1'b0}};
        end
        else begin
            if ((src_rdy_o==1'b1) && (src_vld==1'b1)) begin
                buf_empty <=`U_DLY 1'b0;
                buf_array <=`U_DLY src_dat;
            end
            else if ((buf_empty==1'b0) && (dst_rdy==1'b1))
                buf_empty <=`U_DLY 1'b1;
            else
                ;
        end
    end

    assign src_rdy = src_rdy_o;

    assign dst_vld = ~buf_empty;
    assign dst_dat = buf_array;
    
end
else if (RS_MODE=="BID_RS_FULL_RATE") begin:G_BID_RS_FULL_RATE
    // dst_vld is reg output
    // dst_dat is little combinational logic output
    // src_rdy is reg output
    // data delay is at least 1 clock
    // buffer resource is DAT_BW*2
    // full rate pipeline

    wire                                        src_rdy_o;
    wire                                        dst_vld_o;
    wire                                        buf_wen;
    wire                                        buf_ren;
    reg                                         buf_wptr;
    reg                                         buf_rptr;
    reg                                         buf_empty;
    reg                                         buf_full;
    reg                 [DAT_BW-1:0]            buf_array[1:0];

    assign src_rdy_o = ~buf_full;
    assign dst_vld_o = ~buf_empty;

    assign buf_wen = src_vld & src_rdy_o;
    assign buf_ren = dst_rdy & dst_vld_o;
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_wptr <=`U_DLY 1'b0;
        end
        else begin
            if (buf_wen==1'b1)
                buf_wptr <=`U_DLY buf_wptr + 1'b1;
            else
                ;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_rptr <=`U_DLY 1'b0;
        end
        else begin
            if (buf_ren==1'b1)
                buf_rptr <=`U_DLY buf_rptr + 1'b1;
            else
                ;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_empty <=`U_DLY 1'b1;
        end
        else begin
            if ((buf_ren==1'b1) && (buf_wen==1'b0) && (buf_wptr!=buf_rptr))
                buf_empty <=`U_DLY 1'b1;
            else if ((buf_wen==1'b1) && (buf_empty==1'b1))
                buf_empty <=`U_DLY 1'b0;
            else
                ;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_full <=`U_DLY 1'b0;
        end
        else begin
            if ((buf_wen==1'b1) && (buf_ren==1'b0) && (buf_wptr!=buf_rptr))
                buf_full <=`U_DLY 1'b1;
            else if ((buf_ren==1'b1) && (buf_full==1'b1))
                buf_full <=`U_DLY 1'b0;
            else if ((buf_empty==1'b1) && (buf_full==1'b1))     // for reliability
                buf_full <=`U_DLY 1'b0;
            else
                ;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_array[0] <=`U_DLY {DAT_BW{1'b0}};
            buf_array[1] <=`U_DLY {DAT_BW{1'b0}};
        end
        else begin
            if (buf_wen==1'b1)
                buf_array[buf_wptr] <=`U_DLY src_dat;
            else
                ;
        end
    end
    assign dst_dat = buf_array[buf_rptr];
    assign dst_vld = dst_vld_o;

    assign src_rdy = src_rdy_o;

end
else if (RS_MODE=="RDY_RS_FULL_RATE") begin:G_RDY_RS_FULL_RATE
    // dst_vld and dst_dat are all combinational logic output
    // src_rdy is reg output
    // data delay maybe 0 clock
    // buffer resource is DAT_BW*1
    // full rate pipeline

    reg                                         buf_empty;
    reg                 [DAT_BW-1:0]            buf_array;
    wire                                        src_rdy_o;

    assign src_rdy_o = buf_empty;
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            buf_empty <=`U_DLY 1'b1;
            buf_array <=`U_DLY {DAT_BW{1'b0}};
        end
        else begin
            if ((src_vld==1'b1) && (src_rdy_o==1'b1) && (dst_rdy==1'b0)) begin
                buf_empty <=`U_DLY 1'b0;
                buf_array <=`U_DLY src_dat;
            end
            else if ((buf_empty==1'b0) && (dst_rdy==1'b1))
                buf_empty <=`U_DLY 1'b1;
            else
                ;
        end
    end

    assign src_rdy = src_rdy_o;

    assign dst_vld = (buf_empty==1'b0) ? 1'b1      : src_vld;
    assign dst_dat = (buf_empty==1'b0) ? buf_array : src_dat;
    
end
else begin:G_RS_MODE_NULL
`ifdef PARAMETER_NULL_CHECK  
    // if 'RS_MODE' wrong, force compiler to error for attention.
    rs_mode_null u_null();
`endif
end endgenerate


endmodule
